package com.yeskery.nut.core;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>通用请求接口，该接口是描述请求接口，定义了一系列获取请求信息的方法，通过该接口屏蔽了各种应用类型的Request实现，使得可以在Controller中统一进行
 * 请求处理</p>
 * @author sprout
 * 2019-03-13 16:28
 * @version 1.0
 */
public interface Request extends ServerRequest, BasicContext<Object> {

	/**
	 * 获取指定的请求头值
	 * @param key 请求头的key
	 * @return 指定的请求头值
	 */
	List<String> getHeaders(String key);

	/**
	 * 获取指定的请求头值
	 * @param key 请求头的key
	 * @return 指定的请求头值
	 */
	default String getHeader(String key) {
		List<String> headers = getHeaders(key);
		if (headers == null || headers.isEmpty()) {
			return null;
		}
		return headers.get(0);
	}

	/**
	 * 获取所有的请求头集合
	 * @return 所有的请求头集合
	 */
	Map<String, List<String>> getHeaders();

	/**
	 * 获取所有的请求参数 <b>key</b> 集合
	 * @return 所有的请求参数 <b>key</b> 集合
	 */
	Set<String> getParameterKeys();

	/**
	 * 获取请求的方法
	 * @return 请求的方法
	 */
	Method getMethod();

	/**
	 * 获取原始请求的路径
	 * @return 原始请求的路径
	 */
	String getOriginalPath();

	/**
	 * 获取请求的路径
	 * @return 请求的路径
	 */
	String getPath();

	/**
	 * 获取请求的协议
	 * @return 请求的协议
	 */
	String getProtocol();

	/**
	 * 获取所有的 <b>Cookie</b>
	 * @return 所有的 <b>Cookie</b>
	 */
	Cookie[] getCookies();

	/**
	 * 判断指定的 <b>Cookie</b> 是否存在
	 * @param name <b>Cookie</b> 的 Key
	 * @return <code>true</code> 代表存在，<code>false</code> 代表不存在
	 */
	default boolean hasCookie(String name) {
		return Arrays.stream(getCookies()).anyMatch(cookie -> SessionManager.SESSION_NAME.equals(cookie.getName()));
	}

	/**
	 * 获取请求中的文件对象
	 * @param key 请求参数中的 key
	 * @return 请求中的文件对象
	 *
	 * @see MultipartFile
	 */
	default MultipartFile getFile(String key) {
		return getFiles(key).stream().findFirst().orElseThrow(() -> new NutException("MultipartFile Key[" + key + "] Not Found."));
	}

	/**
	 * 获取请求中的文件对象集合
	 * @param key 请求参数中的 key
	 * @return 请求中的文件对象集合
	 *
	 * @see MultipartFile
	 */
	List<MultipartFile> getFiles(String key);

	/**
	 * 获取请求的请求体字节
	 * @return 请求的请求体字节
	 */
	byte[] getBody();

	/**
	 * 获取请求中的指定参数
	 * @param key 请求参数中的 key
	 * @return 请求中的指定参数
	 */
	default String getParameter(String key) {
		return getParameterMap().get(key);
	}

	/**
	 * 获取请求中的指定参数集合
	 * @param key 请求参数中的 key
	 * @return 请求中的指定参数集合
	 */
	default List<String> getParameters(String key) {
		return getParametersMap().get(key);
	}

	/**
	 * 获取请求参数 <code>Map</code> 集合
	 * @return 请求参数 <code>Map</code> 集合
	 */
	default Map<String, String> getParameterMap() {
		return getParametersMap()
				.entrySet()
				.stream()
				.collect(Collectors.toMap(Map.Entry::getKey, e -> {
					List<String> values = e.getValue();
					return values != null && !values.isEmpty() ? values.get(0) : "";
				}));
	}

	/**
	 * 获取请求参数 <code>Map</code> 集合
	 * @return 请求参数 <code>Map</code> 集合
	 */
	Map<String, List<String>> getParametersMap();

	/**
	 * 获取请求中的指定查询参数
	 * @param key 请求查询参数中的 key
	 * @return 请求中的指定查询参数
	 */
	default String getQueryParameter(String key) {
		return getQueryParameterMap().get(key);
	}

	/**
	 * 获取请求查询参数 <code>Map</code> 集合
	 * @return 请求查询参数 <code>Map</code> 集合
	 */
	default Map<String, String> getQueryParameterMap() {
		return getQueryParametersMap()
				.entrySet()
				.stream()
				.collect(Collectors.toMap(Map.Entry::getKey, e -> {
					List<String> values = e.getValue();
					return values != null && !values.isEmpty() ? values.get(0) : "";
				}));
	}

	/**
	 * 获取请求中的指定查询参数集合
	 * @param key 请求查询参数中的 key
	 * @return 请求中的指定查询参数集合
	 */
	default List<String> getQueryParameters(String key) {
		return getQueryParametersMap().get(key);
	}

	/**
	 * 获取请求查询参数 <code>Map</code> 集合
	 * @return 请求查询参数 <code>Map</code> 集合
	 */
	Map<String, List<String>> getQueryParametersMap();

	/**
	 * 从 <code>Request</code> 域中获取对应的 <code>Session</code> 域
	 * @return 获取对应的 <code>Session</code> 域
	 */
	Session getSession();

	/**
	 * 从 <code>Request</code> 域中获取对应的 <code>ServerContext</code> 域
	 * @return 获取对应的 <code>ServerContext</code> 域
	 */
	ServerContext getServerContext();

	/**
	 * 获取请求体是否为空，即不含有请求体
	 * @return <code>true</code> 代表为空，<code>false</code> 代表不为空
	 */
	boolean isEmpty();

	/**
	 * 获取请求体内容（字符串）
	 * @return 请求体内容（字符串）
	 */
	default String getBodyAsString() {
		if (isEmpty()) {
			return null;
		}
		return new String(getBody(), StandardCharsets.UTF_8);
	}
}
